﻿using System;
using MossmanMill.View;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace MossmanMill.DataModel
{
    public class Rack : IDrawable
    {
        public int Id { get; private set; }
        protected internal int RoomId { get; private set; }
        public string Model { get; private set; }
        public string Address { get; private set; }
        public DateTime InstallDate { get; private set; }
        public Slottable SelectedCard {
            get
            {
                if (SelectedIndex < 0 || SelectedIndex > 12) return null;
                return Cards[SelectedIndex];
            }
        }

        private Room _room;
        private Slottable[] _cards;
        public int SelectedIndex;

        public Room Room
        {
            get
            {
                if (_room != null) return _room;
                IDatabaseAccess db = new DBFactory().Make(DBFactory.CLASS_TYPE_DATABASE);
                _room = db.GetRoom(RoomId);
                return _room;
            }
        }
        public Slottable[] Cards
        {
            get {
                if (_cards != null) return _cards;
                var db = DatabaseAccess.Instance;
                var items = db.GetSlottables(this);
                _cards = new Slottable[13];
                foreach (var card in items)
                {
                    _cards[card.Position] = card;
                }
                return _cards;
            }
            set {
                if (value.Length != 13) throw new Exception("The number of cards should be 13, no more, no less. " + value.Length + " card(s) were provided");
                _cards = value;
            }
        }

        public Rack(int id, int roomId, string model, string address)
        {
            SelectedIndex = 0;
            Id = id;
            Model = model;
            Address = address;
            RoomId = roomId;
        }

        public override string ToString()
        {
            return Model + " " + Address;
        }

        public int GetFreeSlots()
        {
            int count = 0;
            for (int i = 0; i < 13; i++)
            {
                if (Cards[i] == null)
                {
                    count++;
                    continue;
                }
                if (Cards[i].SlotType == 'P') i++;
            }
            return count;
        }

        public void Release()
        {
            _room = null;
            _cards = null;
        }

        public void SetCards(Slottable[] modules)
        {
            _cards = modules;
        }

        private const int HEIGHT = 80;
        private const int CARDWIDTH = 40;
        private const int NUMCARDS = 13;

        public void draw(Graphics graphics, Rectangle bounds, float scale = 1.0f, bool drawChildren = false)
        {
            var x = bounds.Left;
            var y = bounds.Top;
            var buffer = new Bitmap(bounds.Width, bounds.Height);
            var g = Graphics.FromImage(buffer);
            int width = (int)(CARDWIDTH);
            int height = (int)(HEIGHT);

            g.FillRectangle(Brushes.LightSalmon, 0, 0, width * 2, height);
            g.DrawRectangle(Pens.Black, 0, 0, width * 2, height);

            g.DrawString(Model, Fonts.smallTimes, Brushes.Black, 14, 0);
            var address = Address.Split(' ');
            g.DrawString(address[0], Fonts.smallTimes, Brushes.Black, 14, 16);
            if (address.Length > 1)
            {
                string s = "";
                for (int i = 1; i < address.Length; i++) s += " " + address[i];
                g.DrawString(s, Fonts.smallTimes, Brushes.Black, 14, 32);
            }
            if (Room != null) g.DrawString(Room.Name, Fonts.smallArial, Brushes.Black, 4, 60);
            g.DrawString("HoneyWell", Fonts.smallCourier, Brushes.Black, 0, 0, Fonts.VERTICAL);

            for (int i = 0; i < NUMCARDS; i++)
            {
                var rect = new Rectangle((i + 2) * width, 0, width, height);
                Slottable slottable = Cards[i];
                if (slottable == null)
                {
                    g.FillRectangle(Brushes.White, rect);
                    g.DrawRectangle(Pens.Black, rect);
                    g.DrawString("SPARE", Fonts.smallCourier, Brushes.Black, rect.Location, Fonts.VERTICAL);
                    g.DrawString(i + "", Fonts.smallCourier, Brushes.Black, rect.Right - 14, rect.Top);
                    continue;
                }
                switch (slottable.SlotType)
                {
                    case 'C':
                        g.FillRectangle(Brushes.LightGoldenrodYellow, rect);
                        g.DrawRectangle(Pens.Black, rect);
                        var card = (Card)slottable;
                        g.DrawString(card.Name, Fonts.smallCourier, Brushes.Black, rect.Location, Fonts.VERTICAL);
                        break;
                    case 'N':
                        g.FillRectangle(Brushes.SpringGreen, rect);
                        g.DrawRectangle(Pens.Black, rect);
                        var nCard = (NetworkCard)slottable;
                        g.DrawString(nCard.Model, Fonts.smallCourier, Brushes.Black, rect.Location, Fonts.VERTICAL);
                        break;
                    case 'P':
                        rect.Width = 80;
                        g.FillRectangle(Brushes.PowderBlue, rect);
                        g.DrawRectangle(Pens.Black, rect);
                        var pCard = (Processor)slottable;
                        g.DrawString(pCard.Model, Fonts.smallCourier, Brushes.Black, rect.Location, Fonts.VERTICAL);
                        g.DrawString(i + "", Fonts.smallCourier, Brushes.Black, rect.Right - 54, rect.Top);
                        i++;
                        break;
                }
                g.DrawString(i + "", Fonts.smallCourier, Brushes.Black, rect.Right - 14, rect.Top);
            }
            if (drawChildren && SelectedCard != null)
            {
                SelectedCard.draw(graphics, x, buffer.Height + y + 20, scale);
                g.DrawRectangle(Pens.Red, new Rectangle(height + SelectedIndex * width, 0, SelectedCard.SlotType == 'P' ? width * 2 : width, height));
            }
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.DrawImage(buffer, x, y, buffer.Width * scale, buffer.Height * scale);
        }

        public void draw(System.Drawing.Graphics graphics, int x, int y, float scale = 1.0f, bool drawChildren = false)
        {
            draw(graphics, new Rectangle(x, y, 15 * CARDWIDTH + 1, HEIGHT + 1), scale, drawChildren);
        }

        public bool interact(Point location, int type, int x, int y, float scale = 1.0f, bool interactWithChildren = false)
        {
            int width = (int)(scale * CARDWIDTH);
            int height = (int)(scale * HEIGHT);
            var rect = new Rectangle(x + width*2, y, width * NUMCARDS, height);
            if (rect.Contains(location) && type == 2)
            {
                int index = (location.X - (x + width*2)) / width;
                if (index < NUMCARDS && index >= 0)
                {
                    SelectedIndex = index;
                    if (SelectedCard == null && index > 0 && Cards[index - 1] != null && Cards[index - 1].SlotType == 'P') SelectedIndex = index - 1;
                }
                return true;
            }

            if (interactWithChildren)
            {
                foreach (Slottable card in Cards) {
                    if (card == null) continue;
                    card.interact(location, type, x, y + height + 20, 1.0f, false);
                }
            }
            if (SelectedCard != null && SelectedCard.SlotType == 'C' && SelectedCard.interact(location, type, x, y + 20 + height, scale))
            {
                MainForm.Instance.initCard((Card)SelectedCard);
                return true;
            }
            return false;
        }

        public void SetRoom(DataModel.Room room)
        {
            _room = room;
        }

        public void SetModel(string model)
        {
            this.Model = model;
        }

        public void SetAddress(string address)
        {
            this.Address = address;
        }

        public bool Equals(Rack rack)
        {
            int c = 0;
            foreach (var card in rack.Cards)
            {
                if (card == null && Cards[c] != null)
                {
                    Console.WriteLine("Card " + c + " is null");
                    return false;
                }
                if (card != null) {
                    if (Cards[c] == null) return false;
                    if (!card.Equals(Cards[c])) return false;
                }
                c++;
            }
            return rack.Model.Equals(Model) &&
                rack.Address.Equals(Address);

        }

        public void SetId(int id)
        {
            this.Id = id;
        }
    }
}
